use std::fmt;
use std::fmt::Display;
use std::fmt::Formatter;

use moss::from_attrs::FromAttrs;
use moss::from_meta::FromMeta;
use proc_macro2::TokenStream;
use quote::quote;
use syn::DeriveInput;
use syn::Result;

#[derive(FromMeta)]
#[name("form_data")]
pub struct FieldAttr {
    pub field_name: Option<String>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RenameCase {
    Snake,
    Kebab,
    Camel,
    Pascal,
    Lower,
    Upper,
}

impl RenameCase {
    pub fn convert_case(self, s: &str) -> String {
        match self {
            Self::Snake => format!("{}", heck::AsSnakeCase(s)),
            Self::Camel => format!("{}", heck::AsLowerCamelCase(s)),
            Self::Kebab => format!("{}", heck::AsKebabCase(s)),
            Self::Pascal => format!("{}", heck::AsPascalCase(s)),
            Self::Lower => s.to_lowercase(),
            Self::Upper => s.to_uppercase(),
        }
    }
}

#[derive(Debug, Clone)]
pub struct InvalidCase;

impl Display for InvalidCase {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "invalid case conversion option")
    }
}

impl<'a> TryFrom<&'a str> for RenameCase {
    type Error = InvalidCase;

    fn try_from(value: &'a str) -> std::result::Result<Self, Self::Error> {
        match value {
            "snake_case" => Ok(Self::Snake),
            "kebab-case" => Ok(Self::Kebab),
            "camelCase" => Ok(Self::Camel),
            "PascalCase" => Ok(Self::Pascal),
            "UPPERCASE" => Ok(Self::Upper),
            "lowercase" => Ok(Self::Lower),
            _ => Err(InvalidCase),
        }
    }
}

#[derive(FromMeta)]
#[name("try_from_multipart")]
pub struct InputAttr {
    pub rename_all: Option<String>,
}

#[derive(FromAttrs)]
pub struct InputAttrs {
    pub attr: Option<InputAttr>,
}

#[derive(FromAttrs)]
pub struct FieldAttrs {
    pub attr: Option<FieldAttr>,
}

pub fn expand(input: &mut DeriveInput) -> Result<TokenStream> {
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    let for_ident = &input.ident;
    let mut form_token = quote!();
    let input_attrs = InputAttrs::try_from(&input.attrs)?;
    let mut rename_all = None;
    if let Some(input_attr) = &input_attrs.attr {
        if let Some(rename_case) = &input_attr.rename_all {
            let rename = RenameCase::try_from(rename_case.as_str());
            match rename {
                Ok(rename_case) => {
                    rename_all = Some(rename_case);
                }
                Err(_) => {
                    return Err(moss::error::Error::custom(format!(
                        "无效的转换规则: {rename_case}"
                    ))
                    .into());
                }
            }
        }
    }
    match &input.data {
        syn::Data::Struct(data_struct) => {
            for field in &data_struct.fields {
                let ty = &field.ty;
                let field_ident = &field.ident;
                let Some(field_ident) = field_ident else {
                    continue;
                };
                let field_attrs = FieldAttrs::try_from(&field.attrs)?;
                let mut name = field_ident.to_string();
                if let Some(rename_all) = rename_all {
                    name = rename_all.convert_case(name.as_str());
                }
                if let Some(field_attr) = field_attrs.attr {
                    if let Some(field_name) = field_attr.field_name {
                        name = field_name;
                    }
                }
                form_token = quote! {
                    #form_token
                    if name.is_empty() {
                        form = <#ty as apikit::rpc::param::FormParts>::form_parts(self.#field_ident, form, #name.to_string());
                    } else {
                        form = <#ty as apikit::rpc::param::FormParts>::form_parts(self.#field_ident, form, format!("{}.{name}", name));
                    }
                }
            }
        }
        syn::Data::Enum(_) => {
            return Err(moss::error::Error::unexpected_type("enum").into());
        }
        syn::Data::Union(_) => {
            return Err(moss::error::Error::unexpected_type("union").into());
        }
    }
    let expand = quote! {
        impl #impl_generics apikit::rpc::param::FormParts for #for_ident #ty_generics #where_clause {
            fn form_parts(self, mut form: reqwest::multipart::Form, name: String) -> reqwest::multipart::Form {
                #form_token
                form
            }
        }
    };
    Ok(expand)
}
